﻿Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Workflow.ComponentModel
Imports System.Workflow.Runtime

Namespace System.Workflow.Runtime.Tracking
    Friend NotInheritable Class PropertyHelper
        ' Methods
        Private Sub New()
        End Sub

        Friend Shared Sub GetAllMembers(ByVal activity As Activity, ByVal items As IList([Of] TrackingDataItem), ByVal annotations As TrackingAnnotationCollection)
            Dim type As Type = activity.GetType
            Dim info As FieldInfo
            For Each info In type.GetFields((BindingFlags.GetField Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance)))))
                If Not PropertyHelper.IsInternalVariable(info.Name) Then
                    Dim item As New TrackingDataItem { _
                        .FieldName = info.Name, _
                        .Data = PropertyHelper.GetRuntimeValue(info.GetValue(activity), activity) _
                    }
                    Dim str As String
                    For Each str In annotations
                        item.Annotations.Add(str)
                    Next
                    items.Add(item)
                End If
            Next
            Dim info2 As PropertyInfo
            For Each info2 In type.GetProperties((BindingFlags.GetProperty Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance)))))
                If (Not PropertyHelper.IsInternalVariable(info2.Name) AndAlso (info2.GetIndexParameters.Length <= 0)) Then
                    Dim item2 As New TrackingDataItem { _
                        .FieldName = info2.Name, _
                        .Data = PropertyHelper.GetRuntimeValue(info2.GetValue(activity, Nothing), activity) _
                    }
                    Dim str2 As String
                    For Each str2 In annotations
                        item2.Annotations.Add(str2)
                    Next
                    items.Add(item2)
                End If
            Next
        End Sub

        Friend Shared Sub GetEnumerationMember(ByVal collection As IEnumerable, ByVal index As Integer, <Out> ByRef obj As Object)
            obj = Nothing
            If (collection Is Nothing) Then
                Throw New ArgumentNullException("collection")
            End If
            Dim enumerator As IEnumerator = collection.GetEnumerator
            Dim num As Integer = 0
            Do While enumerator.MoveNext
                If (num++ = index) Then
                    obj = enumerator.Current
                    Return
                End If
            Loop
            Throw New IndexOutOfRangeException
        End Sub

        Friend Shared Function GetProperty(ByVal name As String, ByVal obj As Object) As Object
            If (name Is Nothing) Then
                Throw New ArgumentNullException("name")
            End If
            If (obj Is Nothing) Then
                Throw New ArgumentNullException("obj")
            End If
            Dim strArray As String() = name.Split(New Char() { "."c })
            Dim o As Object = obj
            Dim i As Integer
            For i = 0 To strArray.Length - 1
                If ((strArray(i) Is Nothing) OrElse (strArray(i).Length = 0)) Then
                    Throw New InvalidOperationException(ExecutionStringManager.TrackingProfileInvalidMember)
                End If
                Dim obj3 As Object = Nothing
                PropertyHelper.GetPropertyOrField(strArray(i), o, obj3)
                If TypeOf o Is Activity Then
                    o = PropertyHelper.GetRuntimeValue(obj3, DirectCast(o, Activity))
                Else
                    o = obj3
                End If
            Next i
            Return o
        End Function

        Friend Shared Sub GetProperty(ByVal name As String, ByVal activity As Activity, ByVal annotations As TrackingAnnotationCollection, <Out> ByRef item As TrackingDataItem)
            item = Nothing
            Dim property As Object = PropertyHelper.GetProperty(name, activity)
            item = New TrackingDataItem
            item.FieldName = name
            item.Data = [property]
            Dim str As String
            For Each str In annotations
                item.Annotations.Add(str)
            Next
        End Sub

        Friend Shared Sub GetPropertyOrField(ByVal name As String, ByVal o As Object, <Out> ByRef obj As Object)
            obj = Nothing
            If (name Is Nothing) Then
                Throw New ArgumentNullException("name")
            End If
            If (o Is Nothing) Then
                Throw New ArgumentNullException("o")
            End If
            Dim type As Type = o.GetType
            Dim str As String = Nothing
            Dim realName As String = Nothing
            Dim flag As Boolean = False
            Dim index As Integer = -1
            If PropertyHelper.TryParseIndex(name, str, index) Then
                flag = True
            Else
                str = name
            End If
            Dim obj2 As Object = Nothing
            If ((Not str Is Nothing) AndAlso (str.Length > 0)) Then
                If Not PropertyHelper.NameIsDefined(str, o, realName) Then
                    Throw New MissingMemberException(o.GetType.Name, str)
                End If
                obj2 = type.InvokeMember(realName, (BindingFlags.GetProperty Or (BindingFlags.GetField Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance))))), Nothing, o, Nothing, CultureInfo.InvariantCulture)
            Else
                obj2 = o
            End If
            If flag Then
                Dim collection As IEnumerable = [TryCast](obj2,IEnumerable)
                If (Not collection Is Nothing) Then
                    PropertyHelper.GetEnumerationMember(collection, index, obj)
                End If
            Else
                obj = obj2
            End If
        End Sub

        Friend Shared Function GetRuntimeValue(ByVal o As Object, ByVal activity As Activity) As Object
            If (o Is Nothing) Then
                Return o
            End If
            Dim obj2 As Object = o
            If TypeOf o Is ActivityBind Then
                If (activity Is Nothing) Then
                    Throw New ArgumentNullException("activity")
                End If
                Return DirectCast(o, ActivityBind).GetRuntimeValue(activity)
            End If
            If TypeOf o Is WorkflowParameterBinding Then
                obj2 = DirectCast(o, WorkflowParameterBinding).Value
            End If
            Return obj2
        End Function

        Private Shared Function IsInternalVariable(ByVal name As String) As Boolean
            Dim strArray As String() = New String() { "__winoe_ActivityLocks_", "__winoe_StaticActivityLocks_", "__winoe_MethodLocks_" }
            Dim str As String
            For Each str In strArray
                If (String.Compare(str, name, StringComparison.Ordinal) = 0) Then
                    Return True
                End If
            Next
            Return False
        End Function

        Private Shared Function NameIsDefined(ByVal name As String, ByVal o As Object, <Out> ByRef realName As String) As Boolean
            realName = Nothing
            Dim type As Type = o.GetType
            Dim member As MemberInfo() = type.GetMember(name, (BindingFlags.GetProperty Or (BindingFlags.GetField Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or BindingFlags.Instance))))))
            If ((member Is Nothing) OrElse (member.Length = 0)) Then
                member = type.GetMember(name, (BindingFlags.GetProperty Or (BindingFlags.GetField Or (BindingFlags.NonPublic Or (BindingFlags.Public Or (BindingFlags.Static Or (BindingFlags.Instance Or BindingFlags.IgnoreCase)))))))
                If ((member Is Nothing) OrElse (member.Length = 0)) Then
                    Return False
                End If
            End If
            If (((member Is Nothing) OrElse (member.Length = 0)) OrElse ((member(0).Name Is Nothing) OrElse (member(0).Name.Length = 0))) Then
                Return False
            End If
            realName = member(0).Name
            Return True
        End Function

        Private Shared Function TryParseIndex(ByVal fullName As String, <Out> ByRef name As String, <Out> ByRef index As Integer) As Boolean
            name = Nothing
            index = -1
            Dim num As Integer = -1
            Dim length As Integer = -1
            Dim i As Integer = (fullName.Length - 1)
            Do While (i > 0)
                If (("]"c = fullName.Chars(i)) AndAlso (-1 = num)) Then
                    num = i
                ElseIf (("["c = fullName.Chars(i)) AndAlso (-1 = length)) Then
                    length = i
                    Exit Do
                End If
                i -= 1
            Loop
            If ((-1 = num) OrElse (-1 = length)) Then
                Return False
            End If
            Dim s As String = fullName.Substring((length + 1), ((num - 1) - length))
            name = fullName.Substring(0, length)
            Return Integer.TryParse(s, index)
        End Function

    End Class
End Namespace

